package com.zdsx.dao;

import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
public class CaffeineRepository {

    @Resource
    private CaffeineCacheManager cacheManager;

    // 默认不过期cache
    static final String defaultCacheName = "ordinary";

    // 过期默认cache
    static final String defaultExpire = "expire";

    // 不过期
    static final long defaultTimeout = -1;

    // 记录过期可以的添加时间使用
    static final String expireKey = ":time";

    /***
     * 获取Cache 没有就创建
     * @param cacheName
     * @return
     */
    public Cache createCache(String cacheName, long timeout) {
        if (!isCache(cacheName)) {
            cacheManager.registerCustomCache(cacheName, caffeineCacheBuilder(timeout).build());
        }
        return cacheManager.getCache(cacheName);
    }


    public Cache getCache(String cacheName) {
        return cacheManager.getCache(cacheName);
    }

    /***
     * 判断Cache是否存在
     * @param cacheName
     * @return
     */
    public boolean isCache(String cacheName) {
        Collection<String> list = cacheManager.getCacheNames();
        if (!list.isEmpty()) {
            return list.stream().anyMatch(d -> d.equals(cacheName));
        }
        return false;
    }

    /***
     * 判断cache中的key是否存在
     * @param cacheName
     * @param key
     * @return
     */
    public boolean exist(String cacheName, String key) {
        if (isCache(cacheName)) {
            Cache cache = getCache(cacheName);
            CaffeineCache caffeine = (CaffeineCache) cache;
            return caffeine.getNativeCache().asMap().keySet().stream().anyMatch(d -> d.equals(key));

        }
        return false;
    }

    /***
     * 判断默认cache中是否有key
     * @param key
     * @return
     */
    public boolean exist(String key) {
        return exist(defaultCacheName, key);
    }

    /***
     * 保存普通key
     * @param key
     * @param value
     */
    public void set(String key, Object value) {
        set(defaultCacheName, key, value);
    }

    /**
     * 保存其他的key
     *
     * @param cacheName
     * @param key
     * @param value
     */
    public void set(String cacheName, String key, Object value) {
        Cache cache = createCache(cacheName, defaultTimeout);
        if (cache != null) {
            cache.put(key, value);
        }
    }

    /***
     * 获取默认cache中key的值
     * @param key
     * @return Object
     */
    public Object get(String key) {
        return get(defaultCacheName, key);
    }

    public Object get(String cacheName, String key) {
        if (isCache(cacheName)) {
            Cache cache = getCache(cacheName);
            if (cache != null) {
                Cache.ValueWrapper value = cache.get(key);
                if (value != null) {
                    return value.get();
                }
            }
        }
        return null;
    }

    /***
     * 获获取默认cache中key的值
     * @param key
     * @param type 返回的类型
     * @param <T>
     * @return
     */
    public <T> T get(String key, Class<T> type) {
        return get(defaultCacheName, key, type);
    }

    /***
     * 获取指定cache中key的值
     * @param cacheName
     * @param key
     * @param type 返回的类型
     * @param <T>
     * @return
     */
    public <T> T get(String cacheName, String key, Class<T> type) {
        if (isCache(cacheName)) {
            Cache cache = getCache(cacheName);
            if (exist(cacheName, key)) {
                return cache.get(key, type);
            }
        }
        return null;
    }

    /***
     * 保存没过期的key
     * @param key
     * @param value
     */
    public void setExpire(String key, Object value, long time) {
        setExpire(defaultExpire, key, value, time);
    }

    /***
     * 设置过期的key
     * @param cacheName 不同过期时间要使用不同的cache
     * @param key key
     * @param value 值
     * @param time 过期时间（单位是秒）
     */
    public void setExpire(String cacheName, String key, Object value, long time) {
        Cache cache = createCache(cacheName, time);
        if (cache != null) {
            cache.put(key + expireKey, System.currentTimeMillis());
            cache.put(key, value);
        }
    }

    public void setExpire(String cacheName, String key, long time) {
        Cache cache = createCache(cacheName, time);
        if (cache != null) {
            cache.put(key + expireKey, System.currentTimeMillis());
            cache.put(key, true);
        }


    }

    public Object getExpire(String key) {
        return getExpire(defaultExpire, key);
    }

    public Object getExpire(String cacheName, String key) {
        return get(cacheName, key);
    }


    /***
     * 获取秒过期的key
     * @param key
     * @param type 放回的类型
     * @param <T>
     * @return
     */
    public <T> T getExpire(String key, Class<T> type) {
        return getExpire(defaultExpire, key, type);
    }

    public <T> T getExpire(String cacheName, String key, Class<T> type) {
        return get(cacheName, key, type);
    }

    /***
     * 删除普通key
     * @param key
     * @return
     */
    public boolean del(String key) {
        return del(defaultCacheName, key);
    }

    /***
     * 删除cache中的key
     * @param cacheName
     * @param key
     * @return
     */
    public boolean del(String cacheName, String key) {
        if (isCache(cacheName)) {
            Cache cache = getCache(cacheName);
            if (cache != null) {
                return cache.evictIfPresent(key);
            }
        }
        return false;
    }

    /***
     * 删除 默认过期cache中的key
     * @param key
     * @return
     */
    public boolean delExpire(String key) {
        return del(defaultExpire, key);
    }

    /***
     * 获取map中的一个key的值
     * @param key
     * @param hashKey
     * @return
     */
    public Object getHashValues(String key, String hashKey) {
        Map map = get(key, Map.class);
        if (map != null) {
            return map.get(hashKey);
        }
        return null;
    }

    /***
     * 获取map中的一个key的值
     * @param key 缓存key
     * @param hashKey map中的key
     * @param type 放回类型
     * @param <T> 放回类型
     * @return
     */
    public <T> T getHashValues(String key, String hashKey, Class<T> type) {
        Map map = get(key, Map.class);
        if (map != null) {
            Object value = map.get(hashKey);
            if (type != null && type.isInstance(value)) {
                return (T) value;
            }
        }
        return null;
    }

    public void putHashValue(String key, String hashKey, Object hashValue) {
        Map map = get(key, Map.class);
        if (map == null) {
            map = new HashMap<>();
        }
        map.put(hashKey, hashValue);
        set(key, map);
    }

    /**
     * 获取key的过期时间 （单位是秒）
     *
     * @param cacheName
     * @param key
     * @return
     */
    public long getExpireTime(String cacheName, String key) {
        if (isCache(cacheName)) {
            Cache cache = getCache(cacheName);
            CaffeineCache caffeine = (CaffeineCache) cache;
            try {
                long time = caffeine.getNativeCache().policy().expireAfterWrite().get().getExpiresAfter(TimeUnit.MILLISECONDS);
                long depositTime = cache.get(key + expireKey, Long.class);
                return TimeUnit.MILLISECONDS.toSeconds((depositTime + time) - System.currentTimeMillis());
            } catch (Exception e) {
            }
        }
        return -1L;
    }

    /***
     * 获取cache中的所有的key
     * @param cacheName
     * @return
     */
    public Set<String> getAllKeys(String cacheName) {
        if (isCache(cacheName)) {
            Cache cache = getCache(cacheName);
            CaffeineCache caffeine = (CaffeineCache) cache;
            Set<Object> set = caffeine.getNativeCache().asMap().keySet();
            if (!set.isEmpty()) {
                return set.stream().map(Object::toString).collect(Collectors.toSet());
            }
        }
//        return Sets.newHashSet();
        return new HashSet<>();
    }

    /***
     * 获取cache中以keyPatten开头的所有key
     * @param cacheName
     * @param keyPatten
     * @return
     */
    public Set<String> getKeys(String cacheName, String keyPatten) {
        Set<String> set = getAllKeys(cacheName);
        if (set != null && !set.isEmpty()) {
            return set.stream().filter(d -> d.contains(keyPatten)).collect(Collectors.toSet());
        }
        return set;
    }

    /***
     * 获取默认cache中以keyPatten开头的所有key
     * @param keyPatten
     * @return
     */
    public Set<String> getKeys(String keyPatten) {
        return getKeys(defaultCacheName, keyPatten);
    }

    /***
     * 配置cache
     * @param timeout 过期时间（单位是秒）
     * @return
     */
    private Caffeine<Object, Object> caffeineCacheBuilder(long timeout) {
        if (timeout > 0) {
            return Caffeine.newBuilder()
                    .initialCapacity(10)
                    .maximumSize(50)
                    .expireAfterWrite(timeout, TimeUnit.SECONDS);
        } else {
            return Caffeine.newBuilder()
                    .initialCapacity(100)
                    .maximumSize(500);
        }
    }
}